package codegen


import . "compiler/ast"
import . "compiler/lexer"
import . "vm"
// import "fmt"


// kind of operands
const (
	ARG_CONST = 1 // const index
	ARG_REG   = 2 // register index
	ARG_UPVAL = 4 // upvalue index
	ARG_RK    = ARG_REG | ARG_CONST
	ARG_RU    = ARG_REG | ARG_UPVAL
	ARG_RUK   = ARG_REG | ARG_UPVAL | ARG_CONST
)


func cgExp(fi *funcInfo, node Exp, a, n int) {  // n的意义是什么?针对cgAssignStat分析

	switch exp := node.(type) {

		case *NilExp:			fi.emitLoadNil(a, n)

		case *FalseExp:			fi.emitLoadBool(a, 0, 0)

		case *TrueExp:			fi.emitLoadBool(a, 1, 0)

		case *IntegerExp:		fi.emitLoadK(a, exp.Val)

		case *FloatExp:			fi.emitLoadK(a, exp.Val)

		case *StringExp:		fi.emitLoadK(a, exp.Str)

		case *ParensExp:		cgExp(fi, exp.Exp, a, 1)

		case *VarargExp:		cgVarargExp(fi, exp, a, n)

		case *FuncDefExp:		cgFuncDefExp(fi, exp, a)

		case *TableConstructorExp:	cgTableConstructorExp(fi, exp, a)

		case *UnopExp:			cgUnopExp(fi, exp, a)

		case *BinopExp:			cgBinopExp(fi, exp, a)

		case *ConcatExp:		cgConcatExp(fi, exp, a)

		case *NameExp:			cgNameExp(fi, exp, a)

		case *TableAccessExp:	cgTableAccessExp(fi, exp, a)

		case *FuncCallExp:		cgFuncCallExp(fi, exp, a, n)
	}
}


func cgVarargExp(fi *funcInfo, node *VarargExp, a, n int) {
	if !fi.isVararg {
		panic("cannot use '...' outside a vararg function")
	}
	fi.emitVararg(a, n)
}


// 先创建新的funcInfo实例
// 并且让它和外围函数的funcInfo实例形成父子关系
// 然后对函数定义表达式进行处理
// 最后生成一条CLOSURE指令
// 前面两行和后面两行代码都比较容易理解
// 对于中间的代码，有两点需要说明
// 第一，函数的固定参数本质上都是局部变量，所以需要预先声明
// 第二，Lua编译器给每个函数都追加了一条RETURN语句
// f[a] := function(args) body end
func cgFuncDefExp(fi *funcInfo, node *FuncDefExp, a int) {
	subFI := newFuncInfo(fi, node)
	fi.subFuncs = append(fi.subFuncs, subFI)

	for _, param := range node.ParList {
		subFI.addLocVar(param)
	}

	cgBlock(subFI, node.Block)
	subFI.exitScope()
	subFI.emitReturn(0, 0)

	bx := len(fi.subFuncs) - 1
	fi.emitClosure(a, bx)
}


func cgTableConstructorExp(fi *funcInfo, node *TableConstructorExp, a int) {
	// 计算数组的长度
	nArr := 0
	for _, keyExp := range node.KeyExps {
		if keyExp == nil {
			nArr++
		}
	}

	// 考虑多重赋值的情况
	nExps := len(node.KeyExps)
	mulRet := nExps > 0 && isVarargOrFuncCall(node.ValExps[nExps-1])
	fi.emitNewTable(a, nArr, nExps-nArr)

	arrIdx := 0
	for i, keyExp := range node.KeyExps {	
		valExp := node.ValExps[i]

		if keyExp == nil { // 数组
			arrIdx++
			tmp := fi.allocReg()
			if i == nExps-1 && mulRet {
				cgExp(fi, valExp, tmp, -1)
			} else {
				cgExp(fi, valExp, tmp, 1)
			}

			if arrIdx % LFIELDS_PER_FLUSH == 0 || arrIdx == nArr {
				n := arrIdx % LFIELDS_PER_FLUSH
				if n == 0 {  // arrIdx的初始值为1, 所以此处n == 0 必然是k * LFIELDS_PER_FLUSH, 且k > 0
					n = LFIELDS_PER_FLUSH
				}
				fi.freeRegs(n)
				c := (arrIdx-1)/LFIELDS_PER_FLUSH + 1		// 分组数量
				if i == nExps-1 && mulRet {
					fi.emitSetList(a, 0, c)  // b == 0会做特殊处理, 参见setList函数
				} else {
					fi.emitSetList(a, n, c)
				}
			}
			continue
		}

		// 散列表部分
		b := fi.allocReg()
		cgExp(fi, keyExp, b, 1)
		c := fi.allocReg()
		cgExp(fi, valExp, c, 1)
		fi.freeRegs(2)

		fi.emitSetTable(a, b, c)
	}
}


// 先分配一个临时变量，然后对表达式求值
// 最后释放临时变量并生成相应的一元运算符指令即可
// r[a] := op exp
func cgUnopExp(fi *funcInfo, node *UnopExp, a int) {
	b := fi.allocReg()
	cgExp(fi, node.Exp, b, 1)
	fi.emitUnaryOp(node.Op, a, b)
	fi.freeReg()
}


// 对于逻辑与和逻辑或表达式
// 因为其求值结果是操作数之一
// 所以需要特殊对待
// 生成TESETSET和MOVE指令
// 对于其它二元运算符表达式
// 给两个操作数分配临时变量并对表达式求值
// 然后生成相应的二元运算符指令并释放临时变量即可
// r[a] := exp1 op exp2
func cgBinopExp(fi *funcInfo, node *BinopExp, a int) {
	switch node.Op {
	case TOKEN_OP_AND, TOKEN_OP_OR:
		b := fi.allocReg()
		cgExp(fi, node.Exp1, b, 1)
		fi.freeReg()
		if node.Op == TOKEN_OP_AND {
			fi.emitTestSet(a, b, 0)
		} else {
			fi.emitTestSet(a, b, 1)
		}
		pcOfJmp := fi.emitJmp(0, 0)

		b = fi.allocReg()
		cgExp(fi, node.Exp2, b, 1)
		fi.freeReg()
		fi.emitMove(a, b)
		fi.fixSbx(pcOfJmp, fi.pc()-pcOfJmp)
	default:
		b := fi.allocReg()
		cgExp(fi, node.Exp1, b, 1)
		c := fi.allocReg()
		cgExp(fi, node.Exp2, c, 1)
		fi.emitBinaryOp(node.Op, a, b, c)
		fi.freeRegs(2)
	}
}


// 对于拼接表达式，语法分析器已经为我们生成了不同的ast节点
// 我们循环处理每一个操作数(分配临时变量、表达式求值)
// 然后释放临时变量
// 生成一条CONCAT指令即可
// r[a] := exp1 .. expn
func cgConcatExp(fi *funcInfo, node *ConcatExp, a int) {
	for _, subExp := range node.Exps {
		a := fi.allocReg()
		cgExp(fi, subExp, a, 1)
	}
	c := fi.usedRegs - 1
	b := c - len(node.Exps) + 1
	fi.freeRegs(c - b + 1)
	fi.emitABC(OP_CONCAT, a, b, c)
}


// 名字表达式的求值结果可能是局部变量、Upvalue或者全局变量
// 如果是局部变量，生成MOVE指令
// 如果是Upvalue，生成GETUPVAL指令
// 如果是全局变量，则转换成表访问表达式，交给cgTabeAccessExp()函数去处理
// r[a] := name
func cgNameExp(fi *funcInfo, node *NameExp, a int) {
	if r := fi.slotOfLocVar(node.Name); r >= 0 {
		fi.emitMove(a, r)
	} else if idx := fi.indexOfUpval(node.Name); idx >= 0 {
		fi.emitGetUpval(a, idx)
	} else {  // x => _ENV['x']
		taExp := &TableAccessExp{
			PrefixExp:	&NameExp{0, "_ENV"},
			KeyExp:		&StringExp{0, node.Name},
		}
		cgTableAccessExp(fi, taExp, a)
	}
}


// 先给表和键分配临时变量并对表达式求值
// 然后生成GETTABLE指令并释放临时变量
// r[a] := prefix[key]
func cgTableAccessExp(fi *funcInfo, node *TableAccessExp, a int) {
	b := fi.allocReg()
	cgExp(fi, node.PrefixExp, b, 1)
	c := fi.allocReg()
	cgExp(fi, node.KeyExp, c, 1)
	fi.emitGetTable(a, b, c)
	fi.freeRegs(2)
}


// 先处理前缀表达式
// 然后依次处理每一个参数表达式
// 最后生成一条CALL指令即可
// r[a] := f(args)
func cgFuncCallExp(fi *funcInfo, node *FuncCallExp, a, n int) {
	nArgs := prepFuncCall(fi, node, a)
	fi.emitCall(a, nArgs, n)
}


// return f(args)
func cgTailCallExp(fi *funcInfo, node *FuncCallExp, a int) {
	nArgs := prepFuncCall(fi, node, a)
	fi.emitTailCall(a, nArgs)
}


func prepFuncCall(fi *funcInfo, node *FuncCallExp, a int) int {
	nArgs := len(node.Args)
	lastArgIsVarargOrFuncCall := false

	cgExp(fi, node.PrefixExp, a, 1)
	// t.a:Name() 格式, 生成self指令
	if node.NameExp != nil {
		fi.allocReg()
		c := 0x100 + fi.indexOfConstant(node.NameExp.Str)
		fi.emitSelf(a, a, c)
		// c := 0x100 + fi.indexOfConstant(node.NameExp)
		// fi.emitSelf(a, a, c)
		// 仅仅使用上述代码, 会导致
		// s = "12345"  s:sub(1, 5) 
		// 运行出错, 原因是参数加载位置出现问题

		// fi.allocReg()
		// c, k := expToOpArg(fi, node.NameExp, ARG_RK)
		// fi.emitSelf(a, a, c)
		// if k == ARG_REG {
		// 	fi.freeRegs(1)
		// }
	} 
	for i, arg := range node.Args {
		tmp := fi.allocReg()
		if i == nArgs-1 && isVarargOrFuncCall(arg) {
			lastArgIsVarargOrFuncCall = true
			cgExp(fi, arg, tmp, -1)
		} else {
			cgExp(fi, arg, tmp, 1)
		}
	}
	fi.freeRegs(nArgs)

	// 如果是t.a:Name()这种语法糖格式, 那么需要把t.a本身作为一个参数, 所以参数需要+1
	if node.NameExp != nil {
		fi.freeReg()
		nArgs++
	}

	// 如果最后一个参数是...或者函数调用, 那么需要特殊处理, 对应于函数调用中nArgs为0的情况
	// 这里为-1, 是因为生成call指令时, nArgs会+1
	if lastArgIsVarargOrFuncCall {
		nArgs = -1
	}

	return nArgs
}


func expToOpArg(fi *funcInfo, node Exp, argKinds int) (arg, argKind int) {
	if argKinds&ARG_CONST > 0 {
		idx := -1
		switch x := node.(type) {
		case *NilExp:
			idx = fi.indexOfConstant(nil)
		case *FalseExp:
			idx = fi.indexOfConstant(false)
		case *TrueExp:
			idx = fi.indexOfConstant(true)
		case *IntegerExp:
			idx = fi.indexOfConstant(x.Val)
		case *FloatExp:
			idx = fi.indexOfConstant(x.Val)
		case *StringExp:
			idx = fi.indexOfConstant(x.Str)
		}
		if idx >= 0 && idx <= 0xFF {
			return 0x100 + idx, ARG_CONST
		}
	}

	if nameExp, ok := node.(*NameExp); ok {
		if argKinds&ARG_REG > 0 {
			if r := fi.slotOfLocVar(nameExp.Name); r >= 0 {
				return r, ARG_REG
			}
		}
		if argKinds&ARG_UPVAL > 0 {
			if idx := fi.indexOfUpval(nameExp.Name); idx >= 0 {
				return idx, ARG_UPVAL
			}
		}
	}

	a := fi.allocReg()
	cgExp(fi, node, a, 1)
	return a, ARG_REG
}
